home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
oper_sys
/
choices
/
choicess.lha
/
CPU.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-02-06
|
11KB
|
481 lines
/*
* This file is part of the Choices Operating System Simulator
* Developed by: The TAPESTRY Parallel Computing Laboratory
* University of Illinois at Urbana-Champaign
* Department of Computer Science
* 1304 W. Springfield Ave.
* Urbana, IL 61801
*
* Copyright (c) 1987, 1988, 1989 The University of Illinois Board of Trustees.
* All Rights Reserved.
* CONFIDENTIAL INFORMATION. Distribution restricted under license agreement.
*
* Author: Gary M. Johnston (johnston@cs.uiuc.edu)
* Project Manager and Principal Investigator: Roy Campbell (roy@cs.uiuc.edu)
*
* Funded by: NSF TAPESTRY Grant No. 1-5-30035, NASA ICLASS Grant
* No. 1-5-25469 and No. NSG1471 and AT&T Metronet Grant No. 1-5-37411.
*/
/*
* CPU.c - Implementation of classes CPUTimer, CPUManager, and CPU.
* Implementation of ThisCPU().
*
* $Header: CPU.c,v 1.4 88/02/18 16:13:08 johnston Exp $
* $Locker: johnston $
*/
#include "task.h"
#include "Assert.h"
#include "Debug.h"
#include "Debug.h"
#include "Clock.h"
#include "Name.h"
#include "CPU.h"
#include "Process.h"
#include "Vector.h"
#include "Exception.h"
#include "SoftwareException.h"
CPUTimer::CPUTimer( CPU * cpu, int ticks )
: ( CatName( CatName( MakeName( "CPUTimer", this ), " for " ),
cpu->getName() ) )
{
Debug("%s: Ctor (%d ticks).\n", getName(), ticks);
Assert(cpu != 0);
Assert(ticks > 0);
/*
* Initialize.
*/
stopped = 0;
/*
* Start the timer and wait for it.
* Interrupt the CPU when time is up, unless we were stopped.
*/
t = new timer( ticks );
(void) t->result();
if ( ! stopped ) {
Debug("%s::ctor interrupting %s.\n", getName(), cpu->getName());
cpu->interrupt(TimerVector);
}
resultis(0);
NotReached();
}
CPUTimer::~CPUTimer()
{
Assert(rdstate() == TERMINATED);
Assert(t->rdstate() == TERMINATED);
}
int
CPUTimer::stop()
{
/*
* Cancel the timer.
* Wait for the task.
* Return the time that was left.
*/
int residual = t->rdtime() - clock;
stopped = 1;
if ( residual < 0 )
Debug("%s::stop %d - %d < 0\n", getName(), t->rdtime(), clock);
Assert(residual >= 0);
Debug("%s::stop.\n", getName());
t->cancel(0);
(void) result();
return (residual);
}
char *
CPUTimer::getName()
{
return (t_name);
}
CPUManager::CPUManager( CPU * cpu )
: ( CatName( CatName( MakeName( "CPUManager",this ), " for " ),
cpu->getName() ) )
{
Debug("%s: Ctor.\n", getName());
Assert(cpu != 0);
/*
* A CPUManager's work is never done...
*/
for (;;) {
/*
* Wait for something to do.
*/
Debug("%s: sleeping.\n", getName(), this);
sleep();
Debug("%s: waking.\n", getName(), this);
/*
* Stop and delete the timer.
* Remember the residual timeslice time.
*/
int residual = 0;
if (cpu->cpuTimer != 0) {
cpu->cpuTimer->stop();
residual = cpu->cpuTimer->result();
delete cpu->cpuTimer;
cpu->cpuTimer = 0;
}
/*
* Handle pending trap.
*/
SoftwareException * thistrap = cpu->whichTrap;
if (thistrap != 0)
{
Debug("%s: off to a handler.\n", getName(), this);
/* Software Traps stop current Process
* so the handle can be called directly
*/
Assert(cpu->currentProcess->getState() == IDLE);
cpu->whichTrap = 0;
thistrap->handle(cpu,0);
}
/*
* Stop the current Process, if there is one.
*/
else
{
Process * currentProcess = cpu->currentProcess;
if ( currentProcess != 0 ) {
Assert(currentProcess->getState() == RUNNING);
Debug("%s: stop %s.\n",
getName(), currentProcess->getName());
currentProcess->stop();
};
};
/*
* Handle pending interrupts.
*/
int vector;
int vectorCount = 0;
while ( (vector = cpu->removeVector()) != -1 ) {
Assert(VectorValid(vector));
Exception * exception = cpu->getException(vector);
Assert(exception != 0);
Debug("%s: vector %d --> %s.\n",
getName(), vector, exception->getName());
exception->handle(cpu,vector);
vectorCount++;
}
Debug("%s: handled %d vectors.\n", getName(), vectorCount);
/*
* Start the (possibly changed) current Process.
*/
Process * currentProcess = cpu->currentProcess;
if ( currentProcess != 0 ) {
Debug("%s: %s.\n", getName(),
currentProcess->getName());
Assert(currentProcess->getState() == IDLE);
int time = residual;
if ( cpu->currentProcessChanged ) {
Debug("%s: new.\n", getName());
cpu->currentProcessChanged = 0;
time = currentProcess->getQuantum();
}
if ( time != RunToCompletion ) {
Assert(cpu->cpuTimer == 0);
Assert(time > 0);
cpu->cpuTimer = new CPUTimer( cpu, time );
}
Debug("%s: start %s (time: %d).\n",
getName(), currentProcess->getName(), time);
currentProcess->start(cpu);
} else {
Debug("%s: no current Process.\n", getName());
}
}
NotReached();
}
char *
CPUManager::getName()
{
return (t_name);
}
/*
* We maintain a linked list of CPU's for debugging purposes.
* We also allocate unique CPU IDs for convenience.
*/
static CPU * CPUList = 0;
static int CPUCount = 0;
static int ClockStarted = 0;
CPU::CPU( char * name )
: ( name ? name : MakeName( "CPU", this ) )
{
/*
* Initialize CPU fields (except manager, done last).
*/
next = CPUList; CPUList = this;
id = CPUCount++;
currentProcess = 0;
idleProcess = 0;
currentProcessChanged = 0;
vectorQueue = new VectorQueue();
exceptionTable = new Exception * [VectorCount];
for ( int i = 0; i < VectorCount; i++ )
exceptionTable[i] = 0;
scheduler = 0;
whichTrap = 0;
Debug("%s::ctor: %d.\n", getName(), id);
/*
* Build and install the ResetException to handle the ResetVector.
*/
ResetException * resetException = new ResetException();
setException(ResetVector, resetException);
/*
* Build and install the TimerException to handle the TimerVector.
*/
TimerException * timerException = new TimerException();
setException(TimerVector, timerException);
/*
* Build and install the TerminateException to handle the
* TerminateVector.
*/
TerminateException * terminateException = new TerminateException();
setException(TerminateVector, terminateException);
/*
* Start the clock, (first CPU only).
*/
int mask = BlockClock();
if ( ! ClockStarted ) {
ClockStarted = 1;
UnblockClock(mask);
Debug("%s::ctor: Starting clock.\n", getName());
StartClock();
} else {
UnblockClock(mask);
}
/*
* Build manager.
*/
cpuManager = new CPUManager(this);
/*
* Wait for the manager to go to sleep.
* This avoids a race condition where interrupts sent to the
* CPU might refrain from waking up the manager (because it is
* still running before its initial sleep), then when the manager
* goes to sleep, there is the possibility that it might never
* be awoken to handle the pending interrupt.
*/
while ( cpuManager->rdstate() == RUNNING ) {
Assert(thistask != 0);
thistask->delay( 0 );
}
Assert( cpuManager->rdstate() == IDLE );
Debug("%s::ctor: manager %s.\n", getName(), cpuManager->getName());
}
CPU::~CPU()
{
/*
* CPUs should not be destroyed, although I suppose they could.
*/
NotReached();
}
void
CPU::setIdleProcess( Process * idleProcess )
{
/*
* Set the idle Process.
*/
Assert(idleProcess != 0);
Debug("%s: idle Process is %s.\n", getName(), idleProcess->getName());
Assert(idleProcess->getState() == IDLE);
Assert(CPU::idleProcess == 0);
CPU::idleProcess = idleProcess;
}
void
CPU::setScheduler( ProcessContainer * scheduler )
{
/*
* Set the scheduler.
*/
Assert(scheduler != 0);
Debug("%s: scheduler is %s.\n", getName(), scheduler->getName());
CPU::scheduler = scheduler;
}
void
CPU::setException( int vector, Exception * exception )
{
/*
* Set the Exception for the specified vector.
*/
Assert(VectorValid(vector));
Assert(exception != 0);
Assert(exceptionTable != 0);
Assert(exceptionTable[vector] == 0);
Debug("%s: set vector %d to %s.\n",
getName(), vector, exception->getName());
exceptionTable[vector] = exception;
}
void
CPU::interrupt( int vector )
{
Debug("%s: interrupt vector %d.\n", getName(), vector);
Assert(VectorValid(vector));
Assert(exceptionTable != 0);
Assert(exceptionTable[vector] != 0);
Assert(vectorQueue != 0);
/*
* Enqueue the vector.
* Wake up the manager, if necessary.
*/
vectorQueue->add(vector);
int mask = BlockClock();
if ( cpuManager->rdstate() == IDLE )
cpuManager->delay(0);
UnblockClock(mask);
/*
* Let someone else run.
* Necessary? Probably not.
*/
thistask->delay(0);
}
void
CPU::trap( SoftwareException * exception )
{
Debug("%s: software trap %d.\n", getName(), (int) exception);
Assert(exception != 0);
Assert(!((ProcessTask *) thistask)->isPreemptable());
/*
* Enqueue the vector.
* Wake up the manager, if necessary.
*/
whichTrap = exception;
if ( cpuManager->rdstate() == IDLE )
cpuManager->delay(0);
/*
* Software traps block the running process immediately
* so the second loop keeps this Process busy until the Manager
* stops this Process.
*/
currentProcess->stop();
Debug("%s: software trap returns%d.\n", getName(), (int) exception);
}
CPU::getID()
{
/*
* Return the ID.
*/
return (id);
}
void
CPU::add( Process * newProcess )
{
/*
* Make the new Process the current Process.
* We expect that there is initially no current Process.
*/
Assert(newProcess != 0);
Assert(currentProcess == 0);
Debug("%s: add %s.\n", getName(), newProcess->getName());
currentProcess = newProcess;
currentProcessChanged = 1;
}
Process *
CPU::remove()
{
/*
* Remove and return the current Process.
*/
Assert(currentProcess != 0);
Process * process = currentProcess;
currentProcess = 0;
currentProcessChanged = 1;
Debug("%s: remove %s.\n", getName(), process->getName());
return (process);
}
int
CPU::isEmpty()
{
return (currentProcess == 0);
}
int
CPU::removeVector()
{
/*
* Dequeue the next pending vector from the VectorQueue.
* Getting -1 means there are no pending vectors.
*/
int vector = vectorQueue->remove();
Assert(VectorValid(vector) || (vector == -1));
return (vector);
}
Exception *
CPU::getException( int vector )
{
/*
* Return the Exception corresponding to the specified vector.
*/
Assert(VectorValid(vector));
Assert(exceptionTable != 0);
Exception * exception = exceptionTable[vector];
Assert(exception != 0);
return (exception);
}
ProcessContainer *
CPU::getScheduler()
{
return (scheduler);
}
int
CPU::managerRunning()
{
Assert(cpuManager != 0);
Assert(thistask != 0);
return (thistask == cpuManager);
}
CPU *
ThisCPU()
{
int mask = BlockClock();
Assert(IsAProcessTask(thistask));
ProcessTask * pt = (ProcessTask *) thistask;
Process * p = pt->getProcess();
Assert(p != 0);
CPU * cpu = p->getCPU();
Assert(cpu != 0);
(void) UnblockClock(mask);
return (cpu);
}